package com.jinyang.jetpackdemo.activity

import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.jinyang.jetpackdemo.R
import com.jinyang.jetpackdemo.paging3.ApiService
import com.jinyang.jetpackdemo.util.LjyLogUtil
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.components.ApplicationComponent
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.qualifiers.ApplicationContext
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Qualifier
import javax.inject.Singleton

//5. 将依赖项注入 Android 类: 在 Application 类中设置了 Hilt 且有了应用级组件后，Hilt 可以为带有 @AndroidEntryPoint 注释的其他 Android 类提供依赖项
//Hilt 目前支持以下 Android 类： Application（通过使用 @HiltAndroidApp） ,Activity ,Fragment ,View ,Service ,BroadcastReceiver
//如果使用 @AndroidEntryPoint 为某个 Android 类添加注释，则还必须为依赖于该类的 Android 类添加注释。例如，如果您为某个 Fragment 添加注释，则还必须为使用该 Fragment 的所有 Activity 添加注释
@AndroidEntryPoint
class HiltActivity : AppCompatActivity() {
    //如需从组件获取依赖项，请使用 @Inject 注释执行字段注入
    //注意：由 Hilt 注入的字段不能为私有字段
    @Inject
    lateinit var cpu11: CPU

    @Inject
    lateinit var cpu12: CPU

    @Inject
    lateinit var cpu21: CPU2

    @Inject
    lateinit var cpu22: CPU2

    @Inject
    lateinit var phone3: Phone3

//    @Inject
//    lateinit var phone4: Phone4

    @Inject
    lateinit var phone5: Phone5

    @Inject
    lateinit var okHttpClient: OkHttpClient

    @Inject
    lateinit var retrofit: Retrofit


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_hilt)
        val phone1 = Phone1()
        phone1.use()
        val phone2 = Phone2()
        val cpu2 = CPU()
        phone2.cpu = cpu2
        phone2.use()
        cpu11.name = "骁龙888"
        cpu12.name = "骁龙999"
        cpu11.run()
        cpu12.run()

        cpu21.name = "天玑1000"
        cpu22.name = "天玑2000"
        cpu21.run()
        cpu22.run()

        phone3.cpu.name = "麒麟990"
        phone3.use()

        //phone4.use()

        phone5.use()
    }
}

//为了执行字段注入，Hilt 需要知道如何从相应组件提供必要依赖项的实例。“绑定”包含将某个类型的实例作为依赖项提供所需的信息。
//向 Hilt 提供绑定信息的一种方法是构造函数注入。在某个类的构造函数中使用 @Inject 注释，以告知 Hilt 如何提供该类的实例：
class CPU @Inject constructor() {
    var name: String = ""

    fun run() {
        LjyLogUtil.d("$name run...")
    }
}

//组件作用域:Hilt默认会为每次的依赖注入行为都创建不同的实例。
@Singleton
class CPU2 @Inject constructor() {
    var name: String = ""

    fun run() {
        LjyLogUtil.d("$name run...")
    }
}

class Phone1 {
    val cpu = CPU()
    fun use() {
        cpu.run()
    }
}

class Phone2 {
    lateinit var cpu: CPU
    fun use() = cpu.run()
}

class Phone3 @Inject constructor(val cpu: CPU) {
    fun use() {
        cpu.run()
    }
}


interface ICPU {
    fun run()
}


class KylinCPU @Inject constructor() : ICPU {
    override fun run() {
        LjyLogUtil.d("kylin run...")
    }
}

class SnapdragonCPU @Inject constructor() : ICPU {
    override fun run() {
        LjyLogUtil.d("snapdragon run...")
    }
}

class Phone4 @Inject constructor(val cpu: ICPU) {
    fun use() {
        cpu.run()
    }
}

//@Module
//@InstallIn(ActivityComponent::class)
//abstract class CPUModel {
//    @Binds
//    abstract fun bindCPU(cpu: KylinCPU): ICPU
//}

//给相同类型注入不同的实例,比如网络请求中可能需要不同配置的OkHttpClient，或者不同BaseUrl的Retrofit
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindKylinCPU

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindSnapdragonCPU

@Module
@InstallIn(ActivityComponent::class)
abstract class CPUModel {

    @BindKylinCPU
    @Binds
    abstract fun bindKylinCPU(cpu: KylinCPU): ICPU

    @BindSnapdragonCPU
    @Binds
    abstract fun bindSnapdragonCPU(cpu: SnapdragonCPU): ICPU
}

class Phone5 @Inject constructor(@BindSnapdragonCPU private val cpu: ICPU) {
    @BindKylinCPU
    @Inject
    lateinit var cpu1: ICPU

    @BindSnapdragonCPU
    @Inject
    lateinit var cpu2: ICPU

    fun use() {
        cpu.run()
        cpu1.run()
        cpu2.run()
    }

    fun use(@BindKylinCPU cpu: ICPU) {
        cpu.run()
    }
}

class Test1 @Inject constructor(@ApplicationContext private val context: Context)

class Test2 @Inject constructor(@ActivityContext private val context: Context)

class Test3 @Inject constructor(val application: Application)

class Test4 @Inject constructor(val activity: Activity)


//第三方类的依赖注入
//如果我们想要在MainActivity中使用OkHttp发起网络请求，通常会创建一个OkHttpClient的实例。不过原则上OkHttpClient的实例又不应该由Activity去创建，那么很明显，这个时候使用依赖注入是一个非常不错的解决方案。
@Module
@InstallIn(ApplicationComponent::class)
class NetworkModel {

    companion object {
        private const val BASE_URL = "https://api.github.com/"
    }

    @Singleton
    @Provides
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }

    //组件作用域:Hilt默认会为每次的依赖注入行为都创建不同的实例。
    @Singleton
    @Provides
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl(BASE_URL)
            .client(okHttpClient)
            .build()
    }

    @Singleton
    @Provides
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient().newBuilder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(90, TimeUnit.SECONDS)
            .build()
    }
}

